/*
  tempgraph: A utility for plotting historical climatology graphs
  Copyright (C) 2009-2013 Bob Mottram
  bob@sluggish.dyndns.org

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "tempgraph.h"
#include "dialogdata.h"
#include "dialogabout.h"
#include "ui_tempgraph.h"
#include "derivedfromqthread.h"
#include "threadcalculate.h"

TempGraph::TempGraph(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::TempGraph)
{
    ui->setupUi(this);    

    // check that the command line program is installed
    if (!fileExists(QString(TEMPGRAPH_BINARY))) {
        QMessageBox::information(this, "tempgraph binary not found",
                                 "The tempgraph command line program " \
                                 "must be installed in order for " \
                                 "this GUI to work");
        close();
        return;
    }

    setRanges();
    setDefaults();
    setupData();

    // When the calculate button is pressed
    connect(ui->pushButtonCalculate, SIGNAL(clicked()),
            this, SLOT(calculate()));
    // When data sources is selected open the data dialog
    connect(ui->actionDataSources,SIGNAL(triggered()),
            this,SLOT(openDataSources()));
    // Save the current image
    connect(ui->actionSaveImage,SIGNAL(triggered()),
            this,SLOT(saveImageAs()));
    // Exit the application
    connect(ui->actionExit,SIGNAL(triggered()),
            this,SLOT(close()));
    // open the about dialog
    connect(ui->actionAbout,SIGNAL(triggered()),
            this,SLOT(openAbout()));
    // Event for detecting mouse click on image
    ui->graphicsView->installEventFilter(this);

    statusLabel.setText("Ready");
    statusLabel.setIndent(3);
    ui->statusBar->addWidget(&statusLabel);

    showingImage=false;
}

TempGraph::~TempGraph()
{
    freeImage();
    delete ui;
}

// remove temporary files
int TempGraph::tidyUp()
{
    QString command, new_filename;
    int retval;

    if (result_filename == "graph_stations.png") {
        new_filename = globals.dataDirectory + QString(PATH_SEPARATOR) + "result.png";
        command = QString(COPY_FILE) + " " + result_filename + " " + new_filename;
        retval = system(command.toStdString().c_str());
        result_filename = new_filename;
    }

    command = QString(DELETE_FILE) + " plot.* station_numbers.txt graph_stations.png world.dat";
    retval = system(command.toStdString().c_str());
    return retval;
}

// parses the station numbers and returns lists of valid and rejected numbers
void TempGraph::parseStations(vector<QString> &numbers, vector<QString> &rejected)
{
    QString str,station_numbers = ui->lineEditStationNumbers->text();
    int i;

    numbers.clear();
    rejected.clear();
    str="";
    if (station_numbers.length() > 0) {
        for (i = 0; i < station_numbers.length(); i++) {
            if ((station_numbers.at(i) == ',') ||
                (station_numbers.at(i) == ' ') ||
                (i == station_numbers.length()-1)) {
                if (i == station_numbers.length()-1) {
                    if ((station_numbers.at(i) >= '0') &&
                        (station_numbers.at(i) <= '9')) {
                        str += station_numbers.at(i);
                    }
                }
                if (str.length() == 11) {
                    numbers.push_back(str);
                }
                else {
                    rejected.push_back(str);
                }
                str = "";
                continue;
            }
            if ((station_numbers.at(i) >= '0') &&
                (station_numbers.at(i) <= '9')) {
                str += station_numbers.at(i);
            }
        }
    }
}

int TempGraph::setupData()
{
    QString command;
    int retval=0;

    command = QString(MAKE_DIR) + " " + globals.dataDirectory;
    retval = system(command.toStdString().c_str());

    globals.validData = true;
    if ((!TempGraph::fileExists(globals.ghcnDataFilename)) ||
        (!TempGraph::fileExists(globals.ghcnStationsFilename)) ||
        (!TempGraph::fileExists(globals.ghcnCountryCodesFilename))) {

        if (!TempGraph::fileExists(globals.ghcnDataFilename)) {
            qDebug("Unable to find %s",
                   globals.ghcnDataFilename.toStdString().c_str());
        }
        if (!TempGraph::fileExists(globals.ghcnStationsFilename)) {
            qDebug("Unable to find %s",
                   globals.ghcnStationsFilename.toStdString().c_str());
        }
        if (!TempGraph::fileExists(globals.ghcnCountryCodesFilename)) {
            qDebug("Unable to find %s",
                   globals.ghcnCountryCodesFilename.toStdString().c_str());
        }

        globals.validData = false;
        DialogData * data = new DialogData(this,&globals);
        data->exec();
        delete data;
    }

    return retval;
}

// return the index of the given country
int TempGraph::getCountryIndex(QString countryName)
{
    for (int i = 0; i < (int)country.size(); i++) {
        if (country[i] == countryName.toLower()) {
            return i;
        }
    }
    return -1;
}

// Set some default values
void TempGraph::setDefaults()
{
    // get the current year
    time_t t = time(0);
    struct tm * now = localtime(&t);

    // reference period for calculating temperature anomalies
    ref_year_start = 1961;
    ref_year_end = 1990;

    // default size of the image produced
    image_width = 800;
    image_height = 480;

    // vertical position of the subtitle
    subtitle_vertical_position = 0.92f;

    // set the directory where data is stoerd
    globals.dataDirectory = QDir::homePath() + QString(PATH_SEPARATOR) + ".tempgraph";
    globals.ghcnDataFilename = globals.dataDirectory + QString(PATH_SEPARATOR) + "v3.mean";
    globals.ghcnStationsFilename = globals.dataDirectory + QString(PATH_SEPARATOR) + "wmo.txt";
    globals.ghcnCountryCodesFilename = globals.dataDirectory + QString(PATH_SEPARATOR) + "v3.country.codes";

    ui->spinBoxStartYear->setValue(1900);
    ui->spinBoxEndYear->setValue(1900 + now->tm_year);

    // populate the plot type combo
    graphType.push_back("Average");
    graphType.push_back("Anomaly");
    graphType.push_back("Anomaly Map");
    graphType.push_back("Latitude Averages");
    graphType.push_back("Altitude Averages");
    graphType.push_back("Active Stations");
    graphType.push_back("View Stations in Google Earth");
    graphType.push_back("Specific Station IDs");
    graphType.push_back("Station Distance from Equator");
    graphType.push_back("Station Distance from Equator (Arctic Circle)");
    graphType.push_back("Arctic Circle Average");
    graphType.push_back("Northern Hemisphere Average");
    graphType.push_back("Southern Hemisphere Average");
    graphType.push_back("Equatorial Average");
    graphType.push_back("Arctic Circle Anomaly");
    graphType.push_back("Northern Hemisphere Anomaly");
    graphType.push_back("Southern Hemisphere Anomaly");
    graphType.push_back("Equatorial Anomaly");
    graphType.push_back("Average Station Altitude");
    graphType.push_back("Temperature Distribution");
    graphType.push_back("Temperature Distribution (Arctic Circle)");
    for (int i = 0; i < (int)graphType.size(); i++) {
        ui->comboBoxPlotType->addItem(graphType[i]);
    }
    ui->comboBoxPlotType->setCurrentIndex(0);

    // Population classes
    ui->comboBoxPopulationClass->addItem("<Any>");
    ui->comboBoxPopulationClass->addItem("Rural");
    ui->comboBoxPopulationClass->addItem("Suburban");
    ui->comboBoxPopulationClass->addItem("Urban");

    // populate countries
    country.push_back("<Any>");
    country.push_back("africa");
    country.push_back("algeria");
    country.push_back("angola");
    country.push_back("antigua and barbuda");
    country.push_back("argentina");
    country.push_back("asia");
    country.push_back("austria");
    country.push_back("australia");
    country.push_back("barbados");
    country.push_back("belgium");
    country.push_back("belize");
    country.push_back("benin");
    country.push_back("bolivia");
    country.push_back("botswana");
    country.push_back("bulgaria");
    country.push_back("brazil");
    country.push_back("british virgin islands (u.k.)");
    country.push_back("burkina Faso");
    country.push_back("burundi");
    country.push_back("cameroon");
    country.push_back("canada");
    country.push_back("cape Verde");
    country.push_back("cayman islands (u.k.)");
    country.push_back("chad");
    country.push_back("chile");
    country.push_back("china");
    country.push_back("colombia");
    country.push_back("congo");
    country.push_back("costa rica");
    country.push_back("cuba");
    country.push_back("cyprus");
    country.push_back("czech republic");
    country.push_back("denmark");
    country.push_back("djibouti");
    country.push_back("dominica");
    country.push_back("ecuador");
    country.push_back("el salvador");
    country.push_back("egypt");
    country.push_back("equatorial guinea");
    country.push_back("eritrea");
    country.push_back("estonia");
    country.push_back("ethiopia");
    country.push_back("europe");
    country.push_back("finland");
    country.push_back("france");
    country.push_back("gabon");
    country.push_back("gambia");
    country.push_back("germany");
    country.push_back("ghana");
    country.push_back("greece");
    country.push_back("grenada");
    country.push_back("guadeloupe (france)");
    country.push_back("guatemala");
    country.push_back("guinea bissau");
    country.push_back("guinea");
    country.push_back("guyana");
    country.push_back("honduras");
    country.push_back("hungary");
    country.push_back("india");
    country.push_back("indonesia");
    country.push_back("italy");
    country.push_back("ivory Coast");
    country.push_back("jamaica");
    country.push_back("japan");
    country.push_back("kenya");
    country.push_back("latvia");
    country.push_back("lesotho");
    country.push_back("liberia");
    country.push_back("libya");
    country.push_back("lithuania");
    country.push_back("luxembourg");
    country.push_back("madagascar");
    country.push_back("malawi");
    country.push_back("mali");
    country.push_back("malta");
    country.push_back("martinique (france)");
    country.push_back("mauritania");
    country.push_back("mauritius");
    country.push_back("mexico");
    country.push_back("morocco");
    country.push_back("mozambique");
    country.push_back("namibia");
    country.push_back("netherlands");
    country.push_back("nicaragua");
    country.push_back("niger");
    country.push_back("nigeria");
    country.push_back("north america");
    country.push_back("panama");
    country.push_back("paraguay");
    country.push_back("peru");
    country.push_back("puerto rico (u.s.a.)");
    country.push_back("poland");
    country.push_back("portugal");
    country.push_back("republic of ireland");
    country.push_back("reunion");
    country.push_back("russian federation (asian sector)");
    country.push_back("russian federation (european sector)");
    country.push_back("romania");
    country.push_back("rwanda");
    country.push_back("saint lucia");
    country.push_back("saint kitts and nevis");
    country.push_back("saint vincent and the grenadines");
    country.push_back("saudi arabia");
    country.push_back("senegal");
    country.push_back("seychelles");
    country.push_back("sierra Leone");
    country.push_back("slovakia");
    country.push_back("slovenia");
    country.push_back("somalia");
    country.push_back("south africa");
    country.push_back("south america");
    country.push_back("south korea");
    country.push_back("spain");
    country.push_back("sudan");
    country.push_back("suriname");
    country.push_back("swaziland");
    country.push_back("sweden");
    country.push_back("tanzania");
    country.push_back("the bahamas");
    country.push_back("togo");
    country.push_back("trinidad and tobago");
    country.push_back("tunisia");
    country.push_back("turkey");
    country.push_back("united kingdom");
    country.push_back("united states");
    country.push_back("uruguay");
    country.push_back("uganda");
    country.push_back("venezuela");
    country.push_back("zambia");
    country.push_back("zanzibar");
    country.push_back("zimbabwe");
    for (int i = 0; i < (int)country.size(); i++) {
        ui->comboBoxCountry->addItem(country[i]);
    }
    ui->comboBoxCountry->setCurrentIndex(0);

    result_filename="";
}

// Sets the minimum and maximum values
void TempGraph::setRanges()
{
    ui->spinBoxStartYear->setMaximum(2030);
    ui->spinBoxStartYear->setMinimum(1790);
    ui->spinBoxEndYear->setMaximum(2030);
    ui->spinBoxEndYear->setMinimum(1800);
}

// returns true if the given file exists
bool TempGraph::fileExists(QString filename)
{
    FILE * fp;

    fp = fopen(filename.toStdString().c_str(),"r");
    if (fp != NULL) {
          fclose(fp);
          return true;
    }
    return false;
}

// open the data sources dialog
void TempGraph::openDataSources()
{
    DialogData * data = new DialogData(this,&globals);
    data->exec();
    delete data;
}

// open the about dialog
void TempGraph::openAbout()
{
    DialogAbout * about = new DialogAbout(this,&globals);
    about->exec();
    delete about;
}

void TempGraph::showGraph()
{
    if (!fileExists(result_filename)) {
        printf("File not found: %s\n", result_filename.toStdString().c_str());
        ui->pushButtonCalculate->setEnabled(true);
        statusLabel.setText("Ready");
        return;
    }

    if (result_filename.endsWith(".kml")) {
        // open KML file using Google Earth
        if (fileExists(result_filename)) {
            QUrl url(result_filename);
            QDesktopServices::openUrl(url);
        }
        ui->pushButtonCalculate->setEnabled(true);
        statusLabel.setText("Ready");
        return;
    }

    // free any previous image
    freeImage();

    // load the image
    image.load(result_filename);
    if (image.isNull()) {
        QMessageBox::information(this, "Display image","Error Loading image " + result_filename);
        ui->pushButtonCalculate->setEnabled(true);
        statusLabel.setText("Ready");
        return;
    }
    // display the image
    image_scene = new QGraphicsScene();
    image_item = new QGraphicsPixmapItem(QPixmap::fromImage(image));
    image_scene->setSceneRect(image.rect());
    ui->graphicsView->setScene(image_scene);
    ui->graphicsView->scene()->addItem(image_item);
    ui->graphicsView->fitInView(image.rect()); //, Qt::KeepAspectRatio);
    showingImage = true;
    ui->graphicsView->show();
    ui->pushButtonCalculate->setEnabled(true);
    statusLabel.setText("Ready");
    tidyUp();
}

// calculate button is pressed
int TempGraph::calculate()
{
    QString command;
    vector<QString> station_numbers;

    int retval = 0;

    // check station numbers
    if (ui->comboBoxPlotType->currentText() == "Specific Station IDs") {
        vector<QString> rejected;
        parseStations(station_numbers, rejected);
        if ((station_numbers.size()==0) &&
            (rejected.size()==0)) {
            QMessageBox::information(this, "Plot Specific Station IDs",
                                     "No station identifiers have been provided");
            ui->lineEditStationNumbers->setFocus();
            return -1;
        }
        if (rejected.size() > 0) {
            QMessageBox::information(this, "Invalid station identifiers",
                                     "One or more station identifiers were invalid. " \
                                     "Station identifiers should consist of 11 digits: " \
                                     "a 3 digit country code, a 5 digit station " \
                                     "number and a 3 digit modifier");
            ui->lineEditStationNumbers->setFocus();
            return -1;
        }
    }

    statusLabel.setText("Calculating...");

    ui->pushButtonCalculate->setEnabled(false);

    if (!globals.validData) {
        DialogData * data = new DialogData(this,&globals);
        data->exec();
        delete data;
        ui->pushButtonCalculate->setEnabled(true);
        statusLabel.setText("Ready");
        return -1;
    }

    result_filename = globals.dataDirectory + QString(PATH_SEPARATOR) + "result.png";

    command = "tempgraph --ghcn " + globals.ghcnDataFilename +
            " --countrycodes " + globals.ghcnCountryCodesFilename +
            " --wmo " + globals.ghcnStationsFilename +
            " --width " + QString::number(image_width) +
            " --height " + QString::number(image_height) +
            " --vpos " + QString::number(subtitle_vertical_position);
    if (ui->comboBoxPlotType->currentText() != "Anomaly Map") {
        command += " --start " + ui->spinBoxStartYear->text() +
            " --end " + ui->spinBoxEndYear->text();
    }
    if (ui->checkBoxMinMax->isChecked()) {
        command += " --minmax";
    }
    QString country = ui->comboBoxCountry->currentText();
    if (ui->comboBoxPopulationClass->currentText() != "<Any>") {
        command += " --popclass " + ui->comboBoxPopulationClass->currentText().toLower();
    }
    if ((country != "<Any>") &&
        (country.toLower() != "europe") &&
        (country.toLower() != "north america") &&
        (country.toLower() != "south america")) {
        command += " --countries \"" +
                ui->comboBoxCountry->currentText() + "\"";
    }
    if (country.toLower() == "europe") {
        command += " --area \"60N,10W,37N,30E\"";
    }
    if (country.toLower() == "north america") {
        command += " --area \"75N,169W,10N,55W\"";
    }
    if (country.toLower() == "south america") {
        command += " --area \"10N,90W,55S,35W\"";
    }
    if (country.toLower() == "africa") {
        command += " --area \"35N,20W,35S,52E\"";
    }
    if (country.toLower() == "asia") {
        command += " --area \"80N,35E,5N,179E\"";
    }
    if (ui->comboBoxPlotType->currentText() == "Temperature Distribution") {
        command += " --distribution " + result_filename;
    }
    if (ui->comboBoxPlotType->currentText() == "Temperature Distribution (Arctic Circle)") {
        command += " --distribution " + result_filename;
        command += " --latitudes \"90N,66N\"";
    }
    if (ui->comboBoxPlotType->currentText() == "Average Station Altitude") {
        command += " --avaltitude " + result_filename;
    }
    if (ui->comboBoxPlotType->currentText() == "Latitude Averages") {
        command += " --latitudetemps " + result_filename;
    }
    if (ui->comboBoxPlotType->currentText() == "Altitude Averages") {
        command += " --altitudetemps " + result_filename;
    }
    if (ui->comboBoxPlotType->currentText() == "Active Stations") {
        command += " --activestations " + result_filename;
    }
    if (ui->comboBoxPlotType->currentText() == "Arctic Circle Average") {
        command += " --latitudes \"90N,66N\"";
    }
    if (ui->comboBoxPlotType->currentText() == "Station Distance from Equator (Arctic Circle)") {
        command += " --equatordist " + result_filename;
        command += " --latitudes \"90N,66N\"";
    }
    if (ui->comboBoxPlotType->currentText() == "Station Distance from Equator") {
        command += " --equatordist " + result_filename;
    }
    if (ui->comboBoxPlotType->currentText() == "Northern Hemisphere Average") {
        command += " --latitudes \"90N,0N\"";
    }
    if (ui->comboBoxPlotType->currentText() == "Southern Hemisphere Average") {
        command += " --latitudes \"0S,90S\"";
    }
    if (ui->comboBoxPlotType->currentText() == "Equatorial Hemisphere Average") {
        command += " --latitudes \"10N,10S\"";
    }
    if (ui->comboBoxPlotType->currentText() == "Specific Station IDs") {
        command += " --stations \"";
        for (int i = 0; i < (int)station_numbers.size(); i++) {
            command += station_numbers[i];
            if (i < (int)station_numbers.size()-1) {
                command += ",";
            }
        }
        command += "\"";
    }
    if (ui->comboBoxPlotType->currentText() =="View Stations in Google Earth") {
        command += " --kml " + globals.dataDirectory + QString(PATH_SEPARATOR) + "weather_stations.kml";
    }
    if ((ui->comboBoxPlotType->currentText() == "Anomaly") ||
        (ui->comboBoxPlotType->currentText() == "Anomaly Map") ||
        (ui->comboBoxPlotType->currentText() == "Arctic Circle Anomaly") ||
        (ui->comboBoxPlotType->currentText() == "Northern Hemisphere Anomaly") ||
        (ui->comboBoxPlotType->currentText() == "Southern Hemisphere Anomaly") ||
        (ui->comboBoxPlotType->currentText() == "Equatorial Anomaly")) {
        // ensure that the start and end years are not inside the reference period
        if ((ui->spinBoxStartYear->text().toInt() >= ref_year_start) ||
            (ui->spinBoxEndYear->text().toInt() <= ref_year_end)) {
            QString message = "The start year should be less than " + QString::number(ref_year_start) +
                    " and the end year should be greater than " + QString::number(ref_year_end);
            QMessageBox::information(this, "Time period within reference series",
                                     message);
            ui->pushButtonCalculate->setEnabled(true);
            statusLabel.setText("Ready");
            return -1;
        }
        if (ui->comboBoxPlotType->currentText() == "Northern Hemisphere Anomaly") {
            command += " --latitudes \"90N,0N\"";
        }
        if (ui->comboBoxPlotType->currentText() == "Arctic Circle Anomaly") {
            command += " --latitudes \"90N,66N\"";
        }
        if (ui->comboBoxPlotType->currentText() == "Southern Hemisphere Anomaly") {
            command += " --latitudes \"0S,90S\"";
        }
        if (ui->comboBoxPlotType->currentText() == "Equatorial Anomaly") {
            command += " --latitudes \"10N,10S\"";
        }
        if (ui->comboBoxPlotType->currentText() != "Anomaly Map") {
            command += " --anomalies " + result_filename;
        }
        else {
            command += " --year " + ui->spinBoxEndYear->text() +  " --map " + result_filename;
        }
        command += " --runningaverage --refyears \"" + QString::number(ref_year_start) +
            "," + QString::number(ref_year_end) + "\"";
    }
    else {
        command += " --save " + result_filename;
    }

    qDebug("%s", command.toStdString().c_str());

    // if multiple stations are being plotted use a different filename
    if ((ui->comboBoxPlotType->currentText() == "Specific Station IDs") &&
        (station_numbers.size() > 1)) {
        result_filename = "graph_stations.png";
    }

    // If viewing weather stations in Google Earth then change the filename
    if (ui->comboBoxPlotType->currentText() =="View Stations in Google Earth") {
        result_filename = globals.dataDirectory + QString(PATH_SEPARATOR) + "weather_stations.kml";
    }

    QThread* thread = new QThread;
    ThreadCalculate* calc = new ThreadCalculate();
    calc->command = command;
    calc->image_filename = result_filename;
    calc->moveToThread(thread);
    connect(thread, SIGNAL(started()), calc, SLOT(process()));
    connect(calc, SIGNAL(finished()), thread, SLOT(quit()));
    connect(calc, SIGNAL(finished()), calc, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    connect(calc, SIGNAL(finished()), this, SLOT(showGraph()));
    thread->start();

    return retval;
}

// frees memory used to display the image
void TempGraph::freeImage()
{
    if (showingImage) {
        delete image_item;
        delete image_scene;
        showingImage = false;
    }
}

// change the size of the image when the form is resized
void TempGraph::resizeEvent ( QResizeEvent * event )
{
    ui->graphicsView->fitInView(image.rect());
}

// save the current image
int TempGraph::saveImageAs()
{
    int retval = 0;

    // if no image is showing
    if (!showingImage) {
        QMessageBox::information(this, "Save Image As","You must first plot a graph");
        return -1;
    }

    // get the filename
    QString filename =
        QFileDialog::getSaveFileName(this,
            tr("Enter a filename to save as"),
            QDir::currentPath(),
            tr("PNG Image Files (*.png)"));

    // if no filename was given
    if (filename.length()==0) return -1;

    QString command = QString(COPY_FILE) + " " + result_filename + " " + filename;
    retval = system(command.toStdString().c_str());
    QMessageBox::information(this, "Save Image As","Image saved as " + filename);
    return retval;
}

// opens the current graph within a browser
void TempGraph::openImage()
{
    if (!showingImage) return;

    if (fileExists(result_filename)) {
        QUrl url(result_filename);
        QDesktopServices::openUrl(url);
    }
}

// Mouse click events
bool TempGraph::eventFilter(QObject *obj, QEvent *event)
{
    if(obj == ui->graphicsView && event->type() == QEvent::MouseButtonPress)
    {
        openImage();
    }
    return false;
}
